using System;
using System.Drawing;
using System.Collections.Generic;
using Clandestine.Graphics;
using System.Xml;

namespace Clandestine.Engines.Field
{
	public class Level
	{
		public LayerGroup FloorLayers { get; private set; }
		public LayerGroup SkyLayers { get; private set; }
		public ObjectList Objects { get; private set; }
		public RenderList SkyRenderList { get; private set; }
		public RenderList FloorRenderList { get; private set; }
		public RenderList PerpendicularRenderList { get; private set; }
		public RenderList ObjectSkyRenderList { get; private set; }  // under SkyRenderList, over PerpRenderList!
		public RenderList ObjectFloorRenderList { get; private set; } // over FloorRenderList, under PerpRenderList!
		public int Depth { get { return this.Map.Levels.IndexOf(this); } } 
		public Map Map { get; private set; }
		public Clandestine.Graphics.Graphics Graphics { get; private set; }
		
		private bool visible = true;
		public bool Visible
		{
			get { return visible; } 
			set
			{
				visible = value;
				
				FloorLayers.Visible = value;
				SkyLayers.Visible = value;
				
				Objects.Visible = value;
			}
		}
		
		public Level(Clandestine.Graphics.Graphics g, Map map)
		{
			this.Graphics = g;
			this.Map = map;	
			this.Objects = new ObjectList(this.Map, this);
			
			// Make render lists
			SkyRenderList = new RenderList(g);
			FloorRenderList = new RenderList(g);
			PerpendicularRenderList = new RenderList(g);
			ObjectSkyRenderList = new RenderList(g);
			ObjectFloorRenderList = new RenderList(g);
			
			// Make LayerGroups
			FloorLayers = new MegaSpriteLayerGroup(FloorRenderList, this.Map, this);
			SkyLayers = new MegaSpriteLayerGroup(SkyRenderList, this.Map, this);
		}
		
		public void Load(XmlReader xml, Dictionary<int, int> remapping)
		{
			if ((xml.NodeType != XmlNodeType.Element) || (xml.Name != "level"))
				throw new Exception(":(");
			
			// Get attributes	
			// ignore depth, load in order
			
			// And everything else
			while (xml.Read())
			{
				if (xml.NodeType == XmlNodeType.Element)
                {
                    switch (xml.Name)
                    {
						case "objects":
							
							// Internal loop here to load each object!
							while(xml.Read() && !((xml.NodeType == XmlNodeType.EndElement) && (xml.Name == "objects")))
								if ((xml.NodeType == XmlNodeType.Element) && (xml.Name == "object"))
									this.AddObject(xml, remapping);
							break;
						case "floor":
							this.FloorLayers.Load(xml, "floor", new Point(), remapping);
                    		break;
                    	case "sky":
                    		this.SkyLayers.Load(xml, "sky", new Point(), remapping);
                    		break;
                    }
               	} else if ((xml.NodeType == XmlNodeType.EndElement) && (xml.Name == "level"))
                	return;
			}
		}

		public void RemoveObject(Clandestine.Engines.Field.Object obj)
		{
			// Remove from list & dispose
			this.Objects.Remove(obj);
			obj.Dispose();
		}
				
		public Clandestine.Engines.Field.Object AddObject()
		{
			Object o = new Object(this, this.Map);
			this.Objects.Add(o);
			return o;
		}
										
		public Clandestine.Engines.Field.Object AddObject(string filename)
		{
			Object o = new Object(this, this.Map);
			o.Load(filename);
			this.Objects.Add(o);
			return o;
		}
		
		public Clandestine.Engines.Field.Object AddObject(XmlReader xml)
		{
			return this.AddObject(xml, new Dictionary<int, int>());
		}
		public Clandestine.Engines.Field.Object AddObject(XmlReader xml, Dictionary<int, int> remapping)
		{
			Object obj = new Object(this, this.Map);
			obj.Load(xml, remapping);
			this.Objects.Add(obj);
			return obj;
		}
						
		public void RedrawDirty()
		{
			FloorLayers.RedrawDirty();
			SkyLayers.RedrawDirty();
			lock (Objects)
				foreach (Object o in Objects)
					o.RedrawDirty();
		}
	}
}
